/*
 * Copyright 2009 BioTeam Inc
 * 
 * Licensed under License GNU LESSER GENERAL PUBLIC LICENSE version 3.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.gnu.org/copyleft/lesser.html
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package net.bioteam.appweb.gwt.apps.client.Util;

import com.google.gwt.user.client.Timer;
import com.smartgwt.client.types.Alignment;
import com.smartgwt.client.types.VerticalAlignment;
import com.smartgwt.client.util.Page;
import com.smartgwt.client.widgets.Canvas;
import com.smartgwt.client.widgets.Label;
import com.smartgwt.client.widgets.events.DrawEvent;
import com.smartgwt.client.widgets.events.DrawHandler;
import com.smartgwt.client.widgets.events.ResizedEvent;
import com.smartgwt.client.widgets.events.ResizedHandler;
import com.smartgwt.client.widgets.layout.HLayout;
import com.smartgwt.client.widgets.layout.VLayout;

/**
 * Class that supports masking an entire {@link Canvas} and adds the possibility
 * to display a message during masking and a loading image
 * 
 * @author Mihai Ile (mihai007)
 * 
 */
public class ModalWindow {

	/**
	 * The canvas to be masked
	 */
	private final Canvas canvas;

	/**
	 * Parent for modal layer
	 */
	private HLayout parent;

	/**
	 * The modal layer
	 */
	private VLayout modal;

	/**
	 * The reference to the transparent layer inside the modal HStack
	 */
	private Canvas transparent;

	/**
	 * The exact time when show was called on this modal
	 */
	private long showStartedTime;

	/**
	 * The loading icon, by default "{@link Page#getSkinImgDir()}/loading.gif"
	 */
	private String loadingIcon = null;

	/**
	 * The color, by default white
	 */
	private String messageBoxBgColor = "#fff";

	/**
	 * Holds the default opacity for the background masking color
	 */
	private int opacity = 30;

	/**
	 * Holds the default color for the masking
	 */
	private String maskingColor = "#555";

	/**
	 * Creates a new {@link ModalWindow} given the canvas to be masked (an
	 * {@link Canvas#addChild(Canvas)} will be called to add the masking layer
	 * above the given canvas)
	 * 
	 * @param canvas
	 *            the canvas to be masked
	 */
	public ModalWindow(Canvas canvas) {
		this.canvas = canvas;
		createModalPanel();
	}

	/**
	 * Creates a new {@link ModalWindow} given the canvas to be masked (an
	 * {@link Canvas#addChild(Canvas)} will be called to add the masking layer
	 * above the given canvas)
	 * 
	 * @param canvas
	 *            the canvas to be masked
	 * @param opacity
	 *            the opacity used for modal
	 * @param maskingColor
	 *            the color used for modal
	 */
	public ModalWindow(Canvas canvas, int opacity, String maskingColor) {
		this.canvas = canvas;
		this.opacity = opacity;
		this.maskingColor = maskingColor;
		createModalPanel();
	}

	/**
	 * Mask the {@link Canvas} with a transparent color
	 * 
	 * @param showLoading
	 *            whether to show a box with a loading indicator above the
	 *            background
	 */
	public void show(boolean showLoading) {
		showStartedTime = System.currentTimeMillis();
		insertModalIntoCanvas();
		clearLabel();
		if (showLoading) {
			modal.addMember(createLabel("", showLoading));
		}
		parent.show();
	}

	/**
	 * Mask the {@link Canvas} with a transparent color and display a message
	 * above it
	 * 
	 * @param message
	 *            the message to display above the background
	 * @param showLoading
	 *            whether to show a box with a loading indicator above the
	 *            background
	 */
	public void show(String message, boolean showLoading) {
		showStartedTime = System.currentTimeMillis();
		insertModalIntoCanvas();
		clearLabel();
		if (showLoading || !message.equals("")) {
			modal.addMember(createLabel(message, showLoading));
		}
		parent.show();
	}

	/**
	 * Hide the masking layer from the {@link Canvas}
	 */
	public void hide() {
		long showEndTime = System.currentTimeMillis();
		// don't hide if is showed for less than 1s, avoid flickering
		int delay = 1000;
		if (showEndTime - showStartedTime < delay) {
			new Timer() {
				public void run() {
					_hide();
				}
			}.schedule((int) (delay - (showEndTime - showStartedTime)));
		} else {
			_hide();
		}
	}

	private void _hide() {
		parent.hide();
	}

	private void insertModalIntoCanvas() {
		if (!canvas.contains(parent)) {
			canvas.addChild(parent);
			canvas.addResizedHandler(new ResizedHandler() {
				public void onResized(ResizedEvent event) {
					parent.setWidth(canvas.getWidth());
					parent.setHeight(canvas.getHeight());
				}
			});
		}
	}

	private void clearLabel() {
		Canvas[] children = modal.getChildren();
		for (Canvas canvas : children) {
			if (canvas instanceof Label) {
				Label label = (Label) canvas;
				modal.removeChild(label);
				label.destroy();
			}
		}
	}

	private void createModalPanel() {
		parent = new HLayout();
		parent.setDefaultLayoutAlign(VerticalAlignment.CENTER);
		parent.hide();
		// we need a handler to ensure we always cover the entire canvas
		parent.addDrawHandler(new DrawHandler() {
			public void onDraw(DrawEvent event) {
				parent.setWidth(canvas.getVisibleWidth());
				parent.setHeight(canvas.getVisibleHeight());
			}
		});

		transparent = new Canvas();
		transparent.setWidth100();
		transparent.setHeight100();
		transparent.setBackgroundColor(maskingColor);
		transparent.setOpacity(opacity);
		parent.addChild(transparent);

		modal = new VLayout();
		modal.setDefaultLayoutAlign(Alignment.CENTER);
		modal.setHeight(25); // this is for label height
		modal.setZIndex(transparent.getZIndex() + 2);
		parent.addMember(modal);
		
		insertModalIntoCanvas();
	}

	private Label createLabel(String message, boolean showLoading) {
		final Label label = new Label();
		label.setWrap(false);
		label.setPadding(5);
		label.setWidth(1);
		label.setHeight(1);
		label.setContents(message);
		label.setBackgroundColor(messageBoxBgColor);
		label.setBorder("1px solid #999");
		label.setShowShadow(true);
		label.setShadowSoftness(0);
		label.setShadowOffset(10);
		label.addDrawHandler(new DrawHandler() {
			public void onDraw(DrawEvent event) {
				int visibleWidth = label.getVisibleWidth();
				label.setWidth(visibleWidth);
				label.setMargin(10);
			}
		});
		label.setAlign(Alignment.CENTER);
		if (showLoading) {
			if (loadingIcon != null) { // icon provided by user
				label.setIcon(loadingIcon);
			} else { // show default icon from used skin
				String icon = Page.getSkinImgDir() + "loading.gif";
				label.setIcon(icon);
			}
			if (message.equals("")) { // no spacing, just show the loading icon
				// centered
				label.setIconSpacing(0);
			}
		}
		label.setZIndex(modal.getZIndex() + 2);
		return label;
	}

	/**
	 * Destroy the {@link ModalWindow} freeing up resources
	 */
	public void destroy() {
		parent.destroy();
	}

	/**
	 * Optional icon to be used instead of the default one.
	 * <P>
	 * Specify as the partial URL to an image, relative to the imgDir (by
	 * default "public/images/") of this component
	 * 
	 * @param loadingIcon
	 *            icon URL of new image icon. Default value is <blockquote> "
	 *            {@link Page#getSkinImgDir()}/loading.gif "
	 */
	public void setLoadingIcon(String loadingIcon) {
		this.loadingIcon = loadingIcon;
	}

	/**
	 * The background color for the message/loading box. You can set this
	 * property to an RGB value (e.g. #22AAFF) or a named color (e.g. red) from
	 * a list of browser supported color names.
	 * 
	 * @param backgroundColor
	 *            new background color to set to the message/loading box.
	 *            Default value is "#fff"
	 */
	public void setBackgroundColor(String backgroundColor) {
		this.messageBoxBgColor = backgroundColor;
	}

}
